import django
from django.views import View
from django.urls import path, re_path, include
from django.shortcuts import HttpResponse, render, redirect
from django.utils.safestring import mark_safe
from django.urls import reverse
from .page import Page
from django.db.models import Q
from student.models import *

from django.contrib.auth.models import User
from django.contrib import auth

class ShowList(object):
    def __init__(self, config, data_list, request):
        self.config = config
        self.data_list = data_list
        self.request = request

        # actions
        self.actions = self.config.new_actions()

        # print(self.config.list_filter)

    def get_action_list(self):
        temp = []
        for action in self.actions:
            temp.append({
                "name": action.__name__,
                "desc": action.short_description
            })
        return temp

    def get_header(self):
        # 构建表头
        header_list = []
        # print(self.config.new_list_display())
        for field in self.config.new_list_display():
            if callable(field):
                val = field(self.config, header=True)
                header_list.append(val)
            else:
                if field == '__str__':
                    header_list.append(self.config.model._meta.model_name.upper())
                else:
                    header_list.append(self.config.model._meta.get_field(field).verbose_name)
        return header_list

    def get_body(self):
        new_data_list = []
        for obj in self.data_list:
            temp = []
            # print('+++++++++++++', self.config.new_list_display())
            for filed in self.config.new_list_display():  # ['pk, 'name', 'year', edit]

                if callable(filed):
                    val = filed(self.config, obj)
                else:
                    try:
                        from django.db.models.fields.related import ManyToManyField
                        # print('--------', filed)
                        filed_obj = self.config.model._meta.get_field(filed)
                        if isinstance(filed_obj, ManyToManyField):
                            t = []
                            ret = getattr(obj, filed).all()
                            for i in ret:
                                t.append(str(i))
                            # print(t)
                            val = ','.join(t)
                        else:
                            val = getattr(obj, filed)
                        if filed in self.config.list_display_links:
                            model_name = self.config.model._meta.model_name
                            app_label = self.config.model._meta.app_label

                            _url = reverse('%s_%s_change' % (app_label, model_name), args=(obj.pk,))
                            val = mark_safe('<a href="%s">%s</a>' % (_url, val))
                    except Exception as e:
                        val = getattr(obj, filed)
                temp.append(val)

            new_data_list.append(temp)
        return new_data_list

    def get_filter_linktags(self):
        link_dic = {}

        import copy

        for filter_field in self.config.list_filter:
            params = copy.deepcopy(self.request.GET)

            cid = self.request.GET.get(filter_field, 0)

            filter_field_obj = self.config.model._meta.get_field(filter_field)
            # print(type(filter_field_obj))

            from django.db.models.fields.related import ForeignKey
            from django.db.models.fields.related import ManyToManyField

            # print('rel.....', filter_field_obj.remote_field.model.objects.all())
            from django.db.models.fields.related import ManyToManyField, ForeignKey
            if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                data_list = filter_field_obj.remote_field.model.objects.all()
            else:
                data_list = self.config.model.objects.all().values('pk', filter_field)
            temp = []

            # 处理全部标签
            if params.get(filter_field):
                del params[filter_field]
                temp.append("<a href='?%s'>全部</a>" % params.urlencode())
            else:
                temp.append("<a class='active' href='#'>全部</a>")

            # 处理数据标签
            for obj in data_list:
                if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                    pk = obj.pk
                    text = str(obj)
                    params[filter_field] = pk
                else:
                    pk = obj.get('pk')
                    text = obj.get(filter_field)
                    params[filter_field] = text


                _url = params.urlencode()

                if cid == str(pk) or cid == text:
                    link_tag = '<a class="active" href="?%s">%s</a>' % (_url, text)
                else:
                    link_tag = '<a href="?%s">%s</a>' % (_url, text)

                temp.append(link_tag)
            link_dic[filter_field] = temp
            # print(link_dic)

        return link_dic


class ModelStark(object):
    list_display = ['__str__', ]
    list_display_links = []
    modelform_class = None
    search_fields = []
    actions = []
    list_filter = []

    def patch_delete(self, request, queryset):
        queryset.delete()

    patch_delete.short_description = '批量删除'

    def __init__(self, model, site):
        self.model = model  # 用户当前访问的模型表
        self.site = site

    # 删除，编辑，复选框

    def edit(self, obj=None, header=False):
        if header:
            return '操作'
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse('%s_%s_change' % (app_label, model_name), args=(obj.pk,))
        # print('_url:', _url)
        return mark_safe("<a href='%s'>编辑</a>" % _url)

    def deletes(self, obj=None, header=False):
        if header:
            return '删除'
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse('%s_%s_delete' % (app_label, model_name), args=(obj.pk,))
        # print('_url:', _url)
        return mark_safe("<a href='%s'>删除</a>" % _url)

    def checkbox(self, obj=None, header=False):
        if header:
            return mark_safe("<input id='choice' type='checkbox'")
        return mark_safe("<input class='choice_item' type='checkbox' name='selected_pk' value='%s'>" % obj.pk)

    def get_modelform_class(self, id=None, request=None):
        from django.forms import ModelForm
        from django import forms
        model = self.model
        class Form(ModelForm):

            class Meta:
                model = self.model
                fields = '__all__'
                widget = forms.widgets.Input(
                    attrs={"class": "form-control"},
            )


            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
                for field in self.fields.values():
                    # print('field----', field)
                    field.widget.attrs.update({'class': 'form-control'})


        return Form


    def add(self, request):
        ModelFormDemo = self.get_modelform_class()
        form = ModelFormDemo()
        index = request.get_full_path().rfind('/')
        url = request.get_full_path()[:index]
        index1 = url.rfind('/')
        url = url[:index1]
        for bfield in form:
            from django.forms.boundfield import BoundField
            from django.forms.models import ModelChoiceField
            if isinstance(bfield.field, ModelChoiceField):
                bfield.is_pop = True

                # print(bfield.field.queryset.model)  # 一对多或者多对多字段的关联模型表

                related_model_name = bfield.field.queryset.model._meta.model_name
                related_app_label = bfield.field.queryset.model._meta.app_label
                _url = reverse("%s_%s_add" % (related_app_label, related_model_name))
                bfield.url = _url + '?pop_res_id=id_%s' % bfield.name


        if request.method == 'POST':
            form = ModelFormDemo(request.POST)
            if form.is_valid():
                obj = form.save()

                pop_res_id = request.GET.get('pop_res_id')

                if pop_res_id:
                    res = {'pk':obj.pk, 'text':str(obj), 'pop_res_id':pop_res_id}

                    return render(request, 'king_admin/pop.html', locals())
                else:
                    return redirect(url)

        return render(request, 'king_admin/add_view.html', locals())

    def delete(self, request, id):
        path = request.get_full_path()
        import re
        path = re.split('\d+', path)
        self.model.objects.filter(pk=id).delete()

        return redirect(path[0])

    def change(self, request, id):
        ModelFormDemo = self.get_modelform_class(id, request)
        form = ModelFormDemo()
        edit_obj = self.model.objects.filter(pk=id).first()
        path = request.get_full_path()
        import re
        path = re.split('\d+', path)
        for bfield in form:
            from django.forms.boundfield import BoundField
            from django.forms.models import ModelChoiceField
            val = self.model.objects.filter(pk=id).values(bfield.name)
            if isinstance(bfield.field, ModelChoiceField):
                bfield.is_pop = True
                val = self.model.objects.filter(pk=id).values(bfield.name)
                cc = bfield.name
                print('-----', val)
                # (value=val, name=cc, attrs={'selected':'selected'})

                # bfield.field.widget.attrs.update({'selected':'selected'})

                related_model_name = bfield.field.queryset.model._meta.model_name
                related_app_label = bfield.field.queryset.model._meta.app_label
                _url = reverse("%s_%s_add" % (related_app_label, related_model_name))
                bfield.url = _url
            else:
                val = self.model.objects.filter(pk=id).values(bfield.name)
                bfield.field.widget.attrs.update({'value': val[0][bfield.name]})
                print(bfield.field)

        if request.method == 'POST':
            form = ModelFormDemo(instance=edit_obj, data=request.POST)
            if form.is_valid():
                form.save()
                return redirect(path[0])

        # form = ModelFormDemo(instance=edit_obj)
        # form = self.get_new_form(form)

        return render(request, 'king_admin/change_view.html', locals())

    def new_list_display(self):
        temp = []
        temp.append(ModelStark.checkbox)
        temp.extend(self.list_display)
        if not self.list_display_links:
            temp.append(ModelStark.edit)
        temp.append(ModelStark.deletes)
        # print('yemp', temp)
        return temp

    def new_actions(self):
        temp = []
        temp.append(ModelStark.patch_delete)
        temp.extend(self.actions)
        return temp

    def get_search_condition(self, request):
        # search 模块功能的实现
        key_word = request.GET.get('q', '')
        self.key_word = key_word
        from django.db.models import Q
        search_connection = Q()
        if key_word:
            search_connection.connector = 'or'  # 默认是且的关系，可以通过这样设置关系
            for search_field in self.search_fields:
                search_connection.children.append((search_field + "__contains", key_word))
        return search_connection

    def get_filter_condition(self, request):
        # 获取filter构建Q对象
        filter_condition = Q()
        for filter_field, val in request.GET.items():
            if filter_field in self.list_filter:
                filter_condition.children.append((filter_field, val))
        return filter_condition

    def list_view(self, request):

        # action 模块功能实现
        if request.method == 'POST':
            # print('post', request.POST)
            action = request.POST.get('action')
            selected_pk = request.POST.getlist('selected_pk')
            # print('se', selected_pk)
            if action:
                action_func = getattr(self, action)
                queryset = self.model.objects.filter(pk__in=selected_pk)
                action_func(request, queryset)

        # 获取search构建Q对象
        search_connection = self.get_search_condition(request)
        # 获取filter构建Q对象
        filter_conection = self.get_filter_condition(request)

        # 筛选获取当前表的所有数据
        data_list = self.model.objects.all().filter(search_connection).filter(filter_conection)  # [obj, obj, obj...]

        showlist = ShowList(self, data_list, request)

        header_list = showlist.get_header()
        new_data_list = showlist.get_body()

        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        import re
        now_path = request.get_full_path_info()
        if re.search(r'.page=\d+', now_path):
            begin_path, end_path = re.split('.page=\d+', now_path)

            if end_path:
                if end_path.startswith('&'):
                    end_path = end_path.replace(re.match('&', end_path).group(), '?')
                now_path = begin_path + end_path
            else:
                now_path = begin_path

        if now_path.endswith('/'):
            now_path += '?'
        else:
            now_path += '&'
        # request.COOKIES['name'] = 'lzh'
        # print(request.COOKIES)

        list, page_range = Page(request, new_data_list)
        return render(request, 'king_admin/list_view.html', locals())

    def extra_url(self):
        temp = []
        return temp

    def get_urls2(self):

        temp = []

        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        temp.append(path('add/', self.add, name='%s_%s_add' % (app_label, model_name)))
        temp.append(re_path(r'(\d+)/delete/', self.delete, name='%s_%s_delete' % (app_label, model_name)))
        temp.append(re_path(r'(\d+)/change/', self.change, name='%s_%s_change' % (app_label, model_name)))
        temp.append(path('', self.list_view, name='%s_%s_list' % (app_label, model_name)))
        temp.extend(self.extra_url())
        return temp

    @property
    def urls(self):

        return self.get_urls2(), None, None


class StarkSite(object):
    def __init__(self):
        self._registry = {}

    def register(self, model, stark_class=None):
        if not stark_class:
            stark_class = ModelStark

        self._registry[model] = stark_class(model, self)

    def get_urls(self):
        temp = []
        for model, stark_class_obj in self._registry.items():
            model_name = model._meta.model_name
            app_label = model._meta.app_label
            # 分发增删改查
            temp.append(path('%s/%s/' % (app_label, model_name), stark_class_obj.urls))
        return temp

    @property
    def urls(self):

        return self.get_urls(), None, None


site = StarkSite()
